#!/usr/bin/env python
# -*- coding: utf-8 -*-
import random
import urllib
import urllib2
import sys,re
import binascii
import hashlib
import socket
import os
import time
import exceptions
import logging

from Crypto.PublicKey import RSA
import M2Crypto.BIO
import M2Crypto.RSA
from hashlib import md5
from hashlib import sha1
from uuid import uuid1
from threading import RLock
from threading import Thread
from select import select
from Queue import Queue

SIPC_LOGIN_URL = 'https://uid.fetion.com.cn/ssiportal/SSIAppSignInV4.aspx'
SSI_LOGIN_URL = 'https://uid.fetion.com.cn/ssiportal/SSIAppSignInV4.aspx'
PIC_URL = 'http://nav.fetion.com.cn/nav/GetPicCodeV4.aspx'
FETION_LOGIN = '<args><device machine-code="001D0936BCB6" /><caps value="1ff" /><events value="7f" /><user-info mobile-no="%s" user-id="%s"><personal version="0" attributes="v4default" /><custom-config version="0" /><contact-list version="0" buddy-attributes="v4default" /></user-info><credentials domains="fetion.com.cn;m161.com.cn;www.ikuwa.cn;games.fetion.com.cn" /><presence><basic value="400" desc="" /></presence></args>'

class SIPC():
    _header  = ''
    _content = ''
    _version = 'SIP-C/4.0'
    I        = 1
    Q        = 1

    def __init__(self):
        self.__tcp_init()

    def sipc_init(self, init_type):
        '''
        初始化SIPC信令信息
        '''
        self._header = [
                ('F', self._sid),
                ('I', self.I),
                ('Q', '%s %s' % (self.Q, init_type)),
                ]
        self._content = '%s %s %s\r\n' % (init_type, self._domain, self._version)

    def sipc_login(self, ssic, domain):
        # 第一步 获取RSA公钥
        self.sipc_init('R')
        self.sipc_set_content('REG1')
        res_code = self.sipc_send()

        # 第二步
        self.Q = '2'
        self.sipc_init('R')
        self.sipc_set_content('REG2')
        code = self.sipc_send()

        if code == 421:
            self.sipc_init('R')
            self.sipc_set_content('PIC')
            res_code = self.sipc_send()

    def sipc_set_content(self, cmd, *extra):
        sip_body = ''
        # 发起注册请求
        if cmd == 'REG1':
            self._header.append(('CN', '441F7DBA5C3153B61C0660C622F01354'))
            self._header.append(('CL', 'type="pc" ,version="3.6.1860"'))
        # RSA密钥验证
        elif cmd == 'REG2':
            modulus = self.key[:256]
            exponent = self.key[256:]
            keys = {
                'e':long(exponent,16),
                'n':long(modulus,16),
               }
            rsakey = RSA.construct((keys['n'],keys['e']))
            pubkey = rsakey.publickey()
            tmpkey = pubkey.exportKey()
            bio = M2Crypto.BIO.MemoryBuffer(tmpkey)
            rsa = M2Crypto.RSA.load_pub_key_bio(bio)
            userid = self._user_id
            userid='[r]\x11'
            p1 = hash_passwd(self.mobile_pwd, 1)
            p2 = hash_passwd(userid + binascii.a2b_hex(p1), 2)
            aeskey = '4A026855890197CFDF768597D07200B346F3D676411C6F87368B5C2276DCEDD2'
            p3 = self.nonce + binascii.a2b_hex(p2) + binascii.a2b_hex(aeskey)
            response = binascii.b2a_hex(rsa.public_encrypt(p3,M2Crypto.RSA.pkcs1_padding))
            sip_body = FETION_LOGIN % (self.mobile_no, self._user_id)
            self._header.insert(3,('A','Digest algorithm="SHA1-sess-v4",response="%s"' % (response)))
            self._header.append(('L',len(sip_body)))
        elif cmd == 'PIC':
            modulus = self.key[:256]
            exponent = self.key[256:]
            keys = {
                'e':long(exponent,16),
                'n':long(modulus,16),
               }
            rsakey = RSA.construct((keys['n'],keys['e']))
            pubkey = rsakey.publickey()
            tmpkey = pubkey.exportKey()
            userid = self._user_id
            userid='[r]\x11'
            bio = M2Crypto.BIO.MemoryBuffer(tmpkey)
            rsa = M2Crypto.RSA.load_pub_key_bio(bio)
            p1 = hash_passwd(self.mobile_pwd, 1)
            p2 = hash_passwd(userid + binascii.a2b_hex(p1), 2)
            aeskey = '4A026855890197CFDF768597D07200B346F3D676411C6F87368B5C2276DCEDD2'
            p3 = self.nonce + binascii.a2b_hex(p2) + binascii.a2b_hex(aeskey)
            response = binascii.b2a_hex(rsa.public_encrypt(p3,M2Crypto.RSA.pkcs1_padding))
            sip_body = FETION_LOGIN % (self.mobile_no, self._user_id)
            self._header.insert(3,('A','Digest algorithm="SHA1-sess-v4",response="%s"' % (response)))
            self._header.insert(4,('A','Verify response="%s",algorithm="picc-ChangeMachine",type="GeneralPic",chid="%s"' % (self.pic_code, self.pic_id)))
            self._header.append(('L',len(sip_body)))

        for item in self._header:
            self._content = self._content + item[0] + ": " + str(item[1]) + "\r\n"
        self._content+="\r\n"
        self._content+= sip_body
        content = self._content
        print '发送内容','-'*40
        print content
        print '-'*50

    def sipc_send(self):
        res_code = ''
        content = self._content
        # TCP
        self.__tcp_send(content)
        response = self.__tcp_recv()
        print response

        res_code = self.get_response_code(response)
        print "返回码: %d" % res_code
        if res_code == 406:
            print '验证未通过'
        elif res_code == 421:
            #图片验证码
            text = self.pic_verify(response)
        elif res_code == 401:
            self.nonce = re.search(',nonce="(.*)",key=', response).group(1)
            self.key = re.search(',key="(.*)",sign', response).group(1)
            self.sign = re.search(',signature="(.*)"', response).group(1)
        elif res_code == 200:
            print "登录成功！"
            fp=open('/tmp/login','w')
            fp.write(response)
            fp.close()
        return res_code

    def pic_verify(self, data):
        algorithm = re.search('algorithm="(.*)",', data).group(1)
        text = re.search(' text="(.*)" ', data).group(1)
        pic_res = http_send(PIC_URL+"?algorithm=%s" % algorithm)
        header = str(pic_res.info())
        body = pic_res.read()
        self.pic_id = re.search('id="(.*)" pic=',body).group(1)
        pic = re.search('pic="(.*)" />',body).group(1)
        fp = open('/tmp/pic.jpg', 'w')
        fp.write(pic.decode('base64'))
        fp.close()
        self.pic_code = raw_input('请输入验证码：')

    def __tcp_init(self):
        '''
        建立TCP socket连接
        '''
        try:
            self.sock = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        except socket.error,e:
            s = None
        
        #(host,port) = tuple(self.sipc_proxy.split(":"))
        (host,port) = ('221.176.31.36', 8080)
        #port = int(port)

        try:
            self.sock.connect((host,port))
        except socket.error,e:
            print 'tcp_init error'
            self.sock.close()
            #raise PyFetionSocketError(e)

    def __tcp_send(self, content):
        try:
            self.sock.send(content)
        except socket.error, e:
            print 'tcp_send error'
            self.sock.close()

    def __tcp_recv(self):
        total_data = ""
        BUFSIZE = 1024 
        
        buf = self.sock.recv(BUFSIZE)
        total_data += buf
        buf_len = len(buf)
        res_len = self.get_response_len(buf) # L 数据长度
        rnrn_pos = buf.find('\r\n\r\n')
        if rnrn_pos == -1 or (rnrn_pos+4) == len(buf):
            return total_data
        len_count = buf_len - (rnrn_pos + 4)

        if len_count < res_len:
            try:
                while True:
                    if (res_len - len_count) < BUFSIZE:
                        buf = self.sock.recv(res_len - len_count)
                    else:
                        buf = self.sock.recv(BUFSIZE)
                    buf_len  = len(buf)
                    len_count += buf_len
                    total_data += buf
                    if len_count  == res_len:
                        break
            except socket.error, e:
                pass
        return total_data

    def get_response_len(self, response):
        try:
            return int(re.search('L: (\d+)', response).group(1))
        except AttributeError, e:
            return 0

    def get_response_code(self, response):
        cmd = ''
        try:
            self.code = int(re.search("%s (\d{3}) " % self._version, response).group(1))
            self.msg  = re.search("%s (\d{3}) (.*)\r" % self._version, response).group(1)
        except AttributeError, e:
            try:
                cmd = re.search('(.+?) %s' % self._version, response).group(1)
            except AttributeError, e:
                print '解析cmd失败'
            return cmd
        return self.code

def hash_passwd(pwd, hash_type):
    if hash_type == 1:
        src = 'fetion.com.cn:' + pwd
    elif hash_type == 2:
        src = pwd
    value = sha1(src).hexdigest().upper()
    return value

def http_send(url, send_type='get', body=''):
    conn = ''
    headers = {
            'User-Agent':'IIC2.0/PC 3.6.2000',
    }
    opener = urllib2.build_opener()
    urllib2.install_opener(opener)
    if send_type == 'post':
        request = urllib2.Request(url,headers=headers,data=body)
    elif send_type == 'get':
        request = urllib2.Request(url,headers=headers)
    retry = 5
    while retry:
        try:
            conn = urllib2.urlopen(request)
        except urllib2.URLError, e:
            print "错误 ",e
            if hasattr(e,'code'):
                code = e.code
                msg = e.read()
            else:
                code = e.reason.errno
                msg = e.reason.strerror
            
            if code == 405:
                retry = retry - 1
                continue
        break
    return conn

class SSI():
    global SSI_LOGIN_URL
   
    def __init__(self):
        pass

    def ssi_login(self, mobile_no, mobile_pwd):
        url = SSI_LOGIN_URL+"?mobileno="+mobile_no+"&domains=fetion.com.cn%3bm161.com.cn%3bwww.ikuwa.cn&v4digest-type=1&v4digest="+hash_passwd(mobile_pwd, 1)
        response = http_send(url)
        header = str(response.info())
        body   = response.read()
        try:
            self.ssic = re.search("ssic=(.*);", header).group(1)
            self.sip  = re.search("sip:(.*)@", body)
            self.uri  = re.search('uri="(.*)" mobile-no', body).group(1)
            self.user_status = \
                        re.search('user-status="(\d+)"', body).group(1)
            result = {
                    'ssic':re.search("ssic=(.*);", header).group(1),
                    'sid':re.search("sip:(.*)@", body).group(1),
                    'uri':re.search('uri="(.*)" mobile-no', body).group(1),
                    'user_id':re.search('user-id="(\d+)"', body).group(1),
                    'user_status':re.search('user-status="(\d+)"', body).group(1),
                    }
            return result
        except:
            return False

class Fetion(SIPC):
    mobile_no = ''
    mobile_pwd = ''

    _uri = ''
    _sid = ''
    _ssic = ''
    _user_id = ''
    _user_status = ''
    _domain = 'fetion.com.cn'
    _credential = {}  # domain:c

    def __init__(self):
        self.mobile_no = '135xxxxxxxx'
        self.mobile_pwd = 'xxx'

    def fetion_login(self):
        ssi = SSI()
        result = ssi.ssi_login(self.mobile_no, self.mobile_pwd)
        self._ssic = result['ssic']
        self._sid = result['sid']
        self._uri = result['uri']
        self._user_id = result['user_id']
        self._user_status = result['user_status']

        SIPC.__init__(self)
        self.sipc_login(self._ssic, self._domain)

               

if __name__ == '__main__':
    fetion = Fetion()
    fetion.fetion_login()
